/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package play.api.routing.sird

import java.net.URI

import org.specs2.mutable.Specification
import play.core.test.FakeRequest

class UrlContextSpec extends Specification {
  "path interpolation" should {
    "match a plain path" in {
      "match" in {
        "/foo/bar" must beLike {
          case p"/foo/bar" => ok
        }
      }
      "no match" in {
        "/foo/notbar" must beLike {
          case p"/foo/bar" => ko
          case _           => ok
        }
      }
    }

    "match a parameterized path" in {
      "match" in {
        "/foo/testing/bar" must beLike {
          case p"/foo/$id/bar" => id must_== "testing"
        }
      }
      "no match" in {
        "/foo/testing/notbar" must beLike {
          case p"/foo/$id/bar" => ko
          case _               => ok
        }
      }
      "decoded" in {
        "/foo/te%24ting/bar" must beLike {
          case p"/foo/$id/bar" => id must_== "te$ting"
        }
      }
    }

    "match a regex path" in {
      "match" in {
        "/foo/1234/bar" must beLike {
          case p"/foo/$id<[0-9]+>/bar" => id must_== "1234"
        }
      }
      "no match" in {
        "/foo/123n4/bar" must beLike {
          case p"/foo/$id<[0-9]+>/bar" => ko
          case _                       => ok
        }
      }
      "raw" in {
        "/foo/te%24ting/bar" must beLike {
          case p"/foo/$id<[^/]+>/bar" => id must_== "te%24ting"
        }
      }
    }

    "match a star path" in {
      "match" in {
        "/foo/path/to/something" must beLike {
          case p"/foo/$path*" => path must_== "path/to/something"
        }
      }
      "no match" in {
        "/foo/path/to/something" must beLike {
          case p"/foob/$path*" => ko
          case _               => ok
        }
      }
      "raw" in {
        "/foo/path/to/%24omething" must beLike {
          case p"/foo/$path*" => path must_== "path/to/%24omething"
        }
      }
    }

    "match a path with a nested extractor" in {
      "match" in {
        "/foo/1234/bar" must beLike {
          case p"/foo/${int(id)}/bar" => id must_== 1234L
        }
      }
      "no match" in {
        "/foo/testing/bar" must beLike {
          case p"/foo/${int(id)}/bar" => ko
          case _                      => ok
        }
      }
    }

    "match a request" in {
      FakeRequest("GET", "/foo/testing/bar") must beLike {
        case p"/foo/$id/bar" => id must_== "testing"
      }
    }

    "match a uri" in {
      URI.create("/foo/testing/bar") must beLike {
        case p"/foo/$id/bar" => id must_== "testing"
      }
    }

    "match a url" in {
      new URI("http://example.com/foo/testing/bar").toURL must beLike {
        case p"/foo/$id/bar" => id must_== "testing"
      }
    }
  }

  "query string interpolation" should {
    def qs(params: (String, String)*): Map[String, Seq[String]] = {
      params.groupMap(_._1)(_._2)
    }

    "allow required parameter extraction" in {
      "match" in {
        qs("foo" -> "bar") must beLike {
          case q"foo=$foo" => foo must_== "bar"
        }
      }
      "no match" in {
        qs("foo" -> "bar") must beLike {
          case q"notfoo=$foo" => ko
          case _              => ok
        }
      }
    }

    "allow optional parameter extraction" in {
      "existing" in {
        qs("foo" -> "bar") must beLike {
          case q_o"foo=$foo" => foo must beSome("bar")
        }
      }
      "not existing" in {
        qs("foo" -> "bar") must beLike {
          case q_o"notfoo=$foo" => foo must beNone
        }
      }
    }

    "allow seq parameter extraction" in {
      "none" in {
        qs() must beLike {
          case q_s"foo=$foo" => foo must beEmpty
        }
      }
      "one" in {
        qs("foo" -> "bar") must beLike {
          case q_s"foo=$foo" => Seq("bar") must_== Seq("bar")
        }
      }
      "many" in {
        qs("foo" -> "bar1", "foo" -> "bar2", "foo" -> "bar3") must beLike {
          case q_s"foo=$foos" => foos must_== Seq("bar1", "bar2", "bar3")
        }
      }
    }
  }
}
